package gui.chatarea;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Map;

import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
import javax.swing.text.html.parser.ParserDelegator;

import file.TransferFile;

import user.User;
import utils.Configuration;
import utils.FileIconManager;

public class HTMLChatDocument extends HTMLDocument {
	
	public static final String URL_LINK_CLASS = "url" ;
	public static final String FILE_LINK_CLASS = "file" ;
	public static final String USER_LINK_CLASS = "user" ;
	public static final String TRANSFER_FILE_PROGRESS_BAR_CLASS = "transferbar" ;
	public static final String TRANSFER_FILE_LINK_CLASS = "transferfile" ;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private Element root ;
	private User lastSpeaker = null ;
	private Calendar lastTime = null ;
	private Map <String, String> userColors ;
	
	/**
	 * Append the specified html string to the text area, set this.lastSpeaker to null
	 * 
	 * @param html
	 */
	private synchronized void append (String html) {
		try {
			insertAfterEnd(this.root.getElement(this.root.getElementCount() - 1), html) ;
		} 
		catch (BadLocationException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		this.lastSpeaker = null ;
	}
	
	public HTMLChatDocument (StyleSheet css, Map <String, String> userColors) {
		super (css) ;
		this.userColors = userColors ;
		this.setParser(new ParserDelegator()) ;
		this.root = getDefaultRootElement() ;
	}

	/**
	 * Create a bold hyper link html tag corresponding to the specified user.
	 * 
	 * @param user
	 * @return
	 */
	private String getHTMLFromUser (User u) {
		return "<b><a href=\"" + u.getIdentifier() + "\" " 
				+ " class=\"" + USER_LINK_CLASS +"\" "
				+ " style=\"color:" + this.userColors.get(u.getIdentifier()) + ";\">" 
				+ u.getUsername() + "</a></b>" ;
	}
	
	public void printNewUser (User user) {
		append("<p>" + getHTMLFromUser(user) + " a rejoint le système.</p>") ;
	}
	
	public void printUpdateUser (User oldU, User newU) {
		append("<p>" + getHTMLFromUser(oldU) + " est maintenant " + getHTMLFromUser(newU)+ "</p>") ;
	}
	
	public void printRemoveUser (User user) {
		append("<p>" + getHTMLFromUser(user) + " a quitté le système.</p>") ;
	}
	
	public void printMessage (User from, String message) {
		Calendar timeAgo = Calendar.getInstance() ;
		timeAgo.set(Calendar.MINUTE, timeAgo.get(Calendar.MINUTE) - 2) ;
		if (this.lastSpeaker == null || this.lastTime == null || !this.lastSpeaker.equals(from) || this.lastTime.before(timeAgo)) {
			SimpleDateFormat sdf = new SimpleDateFormat("HH'h'mm") ;
			append("<p>" + getHTMLFromUser(from) + " à " + sdf.format(Calendar.getInstance().getTime()) + " : </p>") ;
		}
		append("<p>" + HTMLChatParser.addTag(message) + "</p>") ;	
		this.lastSpeaker = from ;
		this.lastTime = Calendar.getInstance() ;
	}

	/**
	 * Met à jour la balise DIV correspond à uniqueFileID avec la nouvelle Info.
	 * L'information est contenu dans la dernière balise (qui doit être une autre balise <div>) de la balise <div>.
	 * 
	 * @param uniqueFileID
	 * @param newInfo
	 */
	private synchronized void updateFileInfo (String uniqueFileID, String newInfo) {
		Element fileDIV = getElement(uniqueFileID) ;
		try {
			setInnerHTML(fileDIV.getElement(fileDIV.getElementCount() - 1), newInfo) ;
		} 
		catch (BadLocationException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @param tf
	 * @return
	 */
	private String createTransferFileIMGTag (TransferFile tf) {
		try {
			return "<img src=\"file:" + FileIconManager.getFileIcon(tf.getFileName()).replace('\\', '/') + "\" />" ;
		}
		catch (IOException e) {
			return "<img src=\"\" />" ;
		}
	}
	
	/**
	 * Create a TransferFile P info tag with specified args
	 * 
	 * @param info The p message
	 * @param valeur The transfer update (that will set the background)
	 * @param borderColor The border color
	 * @return
	 */
	private String createTransferFilePInfoTag (String info, int valeur, String borderColor) {
		return "<p style='text-align: center; width: 155px; background-image:url(\"file:" + Configuration.getImageFolder()+ "/" + "barre.png" + "\"); "
				+ " background-repeat:no-repeat; background-attachment:fixed; background-position: -" + String.valueOf(2*(100-valeur)) 
				+ "px 0px; border-width:1px; border-color: " + borderColor + "; border-style: solid; "
				+ "'>" + info + "</p>" ;
	}
	
	public void printAskFile (TransferFile tf) {
		append("<div id=\"" + tf.getIdentifier() + "\">"
				+ "<p>Envoi de " + tf.getFileName() + " à " + getHTMLFromUser(tf.getReceiver()) + " : </p>"
				+ "<p>" + createTransferFileIMGTag(tf) + "</p>"
				+ "<p></p></div>") ;
		updateFileInfo(tf.getIdentifier(), createTransferFilePInfoTag("En attente de réponse... ", 0, "green"));
	}
	
	private String getStringFileSize (long fileSize) {
		DecimalFormat df = new DecimalFormat("#.###") ;
		String infos[] = {"o", "ko", "Mo", "Go", "To"} ;
		double dFileSize = fileSize ;
		int count = 1 ;
		while (dFileSize >= 1024 && count < infos.length) {
			dFileSize /= 1024 ;
			count ++ ;
		}
		return df.format(dFileSize) + infos[--count] ;
	}
	
	public void printSaveFile(TransferFile tf) {
		append("<div id=\"" + tf.getIdentifier() + "\">" 
				+ "<p>" + getHTMLFromUser(tf.getSender())
				+ " veut vous envoyer <i>" + tf.getFileName() + "</i> (" + getStringFileSize(tf.getSize()) + ") : </p>"
				+ "<p>" + createTransferFileIMGTag(tf) + "</p>"
				+ "<p></p></div>") ;
		updateFileInfo(tf.getIdentifier(),
				"<p>&nbsp;&nbsp;<a href=\"" + tf.getIdentifier() + "\" class=\"" + TRANSFER_FILE_LINK_CLASS + "\" id=\"true\">Accepter</a>&nbsp;&nbsp;"
						+ "<a href=\"" + tf.getIdentifier() + "\"  class=\"" + TRANSFER_FILE_LINK_CLASS + "\" id=\"false\">Refuser</a></p>") ;
	}
	
	public void printFileAccepted (TransferFile tf) {
		updateFileInfo(tf.getIdentifier(), createTransferFilePInfoTag("Accepté, en attente... ", 0, "green")) ;
	}
	
	public void printFileRefused (TransferFile tf) {
		updateFileInfo(tf.getIdentifier(), createTransferFilePInfoTag("Refusé.", 0, "red")) ;
	}
	
	public void printFileTransferStarted (TransferFile tf) {
		updateFileInfo(tf.getIdentifier(), createTransferFilePInfoTag("0%", 0, "green")) ;
	}
	
	public void printFileTransferUpdated (TransferFile tf, int transfer) {
		updateFileInfo(tf.getIdentifier(), createTransferFilePInfoTag(String.valueOf(transfer) + "%", transfer, "green")) ;
	}
	
	public void printFileTransferSucceed (TransferFile tf) {
		Element fileDIV = getElement(tf.getIdentifier()) ;
		try {
			insertBeforeEnd(fileDIV, "<p>&nbsp;&nbsp;<a href=\"" + tf.getAbsoluteFilePath() + "\" class=\"" + FILE_LINK_CLASS + "\" >Ouvrir</a>&nbsp;&nbsp;"
							+ "<a href=\"" + tf.getFileFolder() + "\"  class=\"" + FILE_LINK_CLASS + "\">Ouvrir le dossier</a></p>") ;
		} 
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void printFileTransferFailed (TransferFile tf) {
		updateFileInfo(tf.getIdentifier(), "Transfert échoué !") ;
	}
	
}
